/**
 * 
 */
package example;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.pi4.locutil.GeoPosition;
import org.pi4.locutil.MACAddress;
import org.pi4.locutil.io.TraceGenerator;
import org.pi4.locutil.trace.Parser;
import org.pi4.locutil.trace.TraceEntry;

/**
 * @author Stenner
 *
 */
public class FingerprintingWrongKNN {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		int k = 2;
		if (args.length > 0){
			try{
				k = Integer.parseInt(args[0]);
			}
			catch (NumberFormatException e) {
				System.err.println("Argument must be an integer");
		        System.exit(1);
			}
		}
		
		String offlinePath = "data/MU.1.5meters.offline.trace", onlinePath = "data/MU.1.5meters.online.trace", apPath = "data/MU.AP.positions" , resultPath = "fingerprintingKNN.txt";
		
		//Construct parsers
		File offlineFile = new File(offlinePath);
		Parser offlineParser = new Parser(offlineFile);
		System.out.println("Offline File: " +  offlineFile.getAbsoluteFile());
		
		File onlineFile = new File(onlinePath);
		Parser onlineParser = new Parser(onlineFile);
		System.out.println("Online File: " + onlineFile.getAbsoluteFile());
		
		File apFile = new File(apPath);
		APParser apParser = new APParser(apFile);
		System.out.println("AP File: " + apFile.getAbsoluteFile());
		
		//Construct trace generator
		TraceGenerator tg;
		
		try {
			int offlineSize = 1;
			int onlineSize = 5;
			tg = new TraceGenerator(offlineParser, onlineParser,offlineSize,onlineSize);
			
			//Generate traces from parsed files
			tg.generate();
				
			//Iterate the trace generated from the offline file
			//List<TraceEntry> radiomap = tg.getOffline();	
			//Iterate the trace generated from the online file
			List<TraceEntry> onlineTraces = tg.getOnline();
			//Iterate the access point file
			List<TraceEntry> accessPoints = apParser.parse();
			
			//create outputfile
			FileWriter fstream = new FileWriter(resultPath);
			BufferedWriter out = new BufferedWriter(fstream);
			for(TraceEntry entry: onlineTraces) {
				//Print out coordinates for the collection point and the number of signal strength samples
				
				//true position:
				System.out.println(entry.getGeoPosition().toStringWithoutOrientation());
				out.write(entry.getGeoPosition().toStringWithoutOrientation() + ";");
				
				//Filter access points from the the entry
				List<MACAddress> KNearest = getKNN(entry, k, accessPoints); 
				//Get a list of coordinates from the entry
				List<GeoPosition> Coordinates = getCoordinates(accessPoints, KNearest);
				//Get the average coordinate
				GeoPosition avgCoordinate = avg(Coordinates);
				
				System.out.println(avgCoordinate.toStringWithoutOrientation() + '\n');
				out.write(avgCoordinate.toStringWithoutOrientation() + '\n');
				//}
			}
			out.close();
			
			/*
			//Iterate the trace generated from the online file
			List<TraceEntry> onlineTrace = tg.getOnline();			
			for(TraceEntry entry: onlineTrace) {
				//Print out coordinates for the collection point and the number of signal strength samples
				System.out.println(entry.getGeoPosition().toString() + " - " + entry.getSignalStrengthSamples().size());
			}
			*/
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Averages k positions.
	 * 
	 * @param values	the positions
	 * @return	the averaged position
	 */
	public static GeoPosition avg(List<GeoPosition> values) {
		int k = values.size();
		if (k < 1)
			throw new IllegalArgumentException("Length must be >= 1");
		double x = 0, y = 0, z = 0, o = 0;
		for (int i = 0; i < k; i++) {
			GeoPosition value = values.get(i);
			x += value.getX();
			y += value.getY();
			z += value.getZ();
			o += value.getOrientation();
		}
		x /= (double) k;
		y /= (double) k;
		z /= (double) k;
		o /= (double) k;
		return new GeoPosition(x, y, z, o % 360.0);
	}

	private static List<GeoPosition> getCoordinates(List<TraceEntry> accessPointList, List<MACAddress> kNearest) {
		List<GeoPosition> Coordinates = new ArrayList<GeoPosition>(); 
		for(MACAddress mac : kNearest){
			for(TraceEntry apentry: accessPointList){
				if(apentry.getId().hashCode() == mac.hashCode()){
					Coordinates.add(apentry.getGeoPosition());
				}	
			}
		}
		return Coordinates;
	}

	/*
	private static Double KNNDistance(HashMap<MACAddress, Double> hm, TraceEntry radiomapentry) {
		Set ref = hm.keySet();
		Iterator it = ref.iterator();

		while (it.hasNext()) {
		  //String file = (String)it.next();
			MACAddress mac = (MACAddress)it.next();
			double m1 = hm.get(mac);
			List<Double> SSs = radiomapentry.getSignalStrengthSamples().getSignalStrengthValues(mac);
			double ss1 = SSs.get(0);
		}return null;
	}*/

	/*private static HashMap<MACAddress, Double> getKNNMap(List<MACAddress> kNearest, TraceEntry entry) {
		HashMap<MACAddress, Double> hm = new HashMap<MACAddress, Double>();
		for(MACAddress mac : kNearest){
			//find the signal strength for the MAC
			List<Double> m1 = entry.getSignalStrengthSamples().getSignalStrengthValues(mac);
			//add an entry to the HashMap
			hm.put(mac, m1.get(0));
		}
		return hm;
	}*/

	private static List<MACAddress> getKNN(TraceEntry entry, int k, List<TraceEntry> knownAPs) throws Exception {
		//Filter access points from the the entry
		List<MACAddress> APs = entry.getSignalStrengthSamples().getSortedAccessPoints();
		List<MACAddress> NearestAPs = new ArrayList<MACAddress>();
		List<MACAddress> recognized = new ArrayList<MACAddress>();
		for(MACAddress ap: APs){
			for(TraceEntry kap: knownAPs){
				if(kap.getId().hashCode() == ap.hashCode())
					recognized.add(ap);
			}
				
		}
		if(recognized.size() < k)
			throw new Exception("Not enough access points in the entry");
		for(int i = 0; i < k; i++){
			NearestAPs.add(recognized.get(i));
		}
		return NearestAPs;
	}
}
